package com.itita.ww2.game.user;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.itita.ww2.core.MessageHandler;
import com.itita.ww2.data.MissionConfig;
import com.itita.ww2.data.PlayerConfig;
import com.itita.ww2.data.shop.PaymentConfig;
import com.itita.ww2.data.shop.PaymentConfig.Category;
import com.itita.ww2.enums.PushCode;
import com.itita.ww2.game.item.ItemResponse;
import com.itita.ww2.game.user.UserRewardDispatch.IUserRewardListener;
import com.itita.ww2.game.user.UserRewardDispatch.RewardType;
import com.itita.ww2.game.user.insideevent.IUserItemChangedListener.ChangedType;
import com.itita.ww2.model.CodeType;
import com.itita.ww2.model.item.AbstractItem;
import com.itita.ww2.model.item.ItemBuilder;
import com.itita.ww2.model.item.ds.ItemValue;
import com.itita.ww2.model.item.enums.ItemPrimaryType;
import com.itita.ww2.model.item.enums.ItemPropType;
import com.itita.ww2.model.item.prop.ItemProp;
import com.itita.ww2.model.user.UserProfile;
import com.itita.ww2.redis.RedisUtil;
import com.itita.ww2.utils.MathUtil;
import com.smartfoxserver.v2.entities.data.ISFSArray;
import com.smartfoxserver.v2.entities.data.ISFSObject;
import com.smartfoxserver.v2.entities.data.SFSArray;
import com.smartfoxserver.v2.entities.data.SFSDataType;
import com.smartfoxserver.v2.entities.data.SFSDataWrapper;
import com.smartfoxserver.v2.entities.data.SFSObject;

/**
 * 玩家背包
 * @author NorthLan
 *
 */
public class UserInventoryManager {
	// private static final Logger logger = LoggerFactory.getLogger(UserInventoryManager.class);
	//
	private UserProfile userProfile;

	private transient Table<String, String, ItemValue> inventory;
	//
	private transient Table<String, String, ItemValue> tempInventory;

	public UserInventoryManager(UserProfile userProfile) {
		this.userProfile = userProfile;
		this.inventory = HashBasedTable.create();
	}

	public void backup() {
		this.tempInventory = this.inventory;
	}

	public void clean() {
		this.tempInventory = null;
	}

	public void rollback() {
		if (this.tempInventory == null) {
			return;
		}
		this.inventory = this.tempInventory;
		clean();
	}

	/**
	 * 是否拥有物品(uuid)
	 */
	public boolean isItemExistsByUUID(String uuid) {
		return inventory.containsRow(uuid);
	}

	/**
	 * 是否拥有物品(ItemId)
	 */
	public boolean isItemExistsByItemId(String itemId) {
		return inventory.containsColumn(itemId);
	}

	public void addItems(ISFSObject itemObj) {
		for (String key : itemObj.getKeys()) {
			if (CodeType.codeValueOf(key) != CodeType.ITEM) {
				continue;
			}
			addItem(key, itemObj.getInt(key));
		}
	}

	/**
	 * 往背包添加一种物品<br>
	 * 按照堆叠规则堆叠
	 */
	public ItemValue addItem(String itemId, int count) {
		if (count == 0) {
			return null;
		}
		if (CodeType.codeValueOf(itemId) != CodeType.ITEM) {
			return null;
		}
		// 存在且可堆叠,调整数量;否则,新建物品
		ItemValue iv = getItemByItemId(itemId);
		if (iv != null && iv.getItem().canStack()) {
			operItemCount(iv, count);
		} else {
			AbstractItem item = new ItemBuilder().genericUUID().setItemId(itemId).build();
			iv = new ItemValue(item, count);
			inventory.put(item.getUuid(), item.getItemId(), iv);
			userProfile.getInsideEventMgr().onUserItemChanged(iv, ChangedType.ADD);
		}
		return iv;
	}

	/**
	 * 获取背包内物品
	 */
	public ItemValue getItemByUUID(String uuid) {
		if (!inventory.containsRow(uuid)) {
			return null;
		}
		return inventory.row(uuid).values().iterator().next();
	}

	/**
	 * 获取背包内物品
	 */
	public ItemValue getItemByItemId(String itemId) {
		if (!inventory.containsColumn(itemId)) {
			return null;
		}
		return inventory.column(itemId).values().iterator().next();// 取第一个
	}

	/**
	 * 获取背包内的prop类型物品
	 */
	public ItemValue getItemProp(ItemPropType propType, int param1) {
		return inventory.values().stream().filter(p -> {
			if (p.getItem().getPrimaryType() != ItemPrimaryType.PROP)
				return false;
			ItemProp prop = (ItemProp) p.getItem();
			if (prop.getPropType() != propType)
				return false;
			return prop.getParam1() == param1;
		}).findAny().orElse(null);
	}

	/**
	 * 批量移除物品
	 */
	public void subItems(ISFSObject itemObj) {
		for (String key : itemObj.getKeys()) {
			operItemCount(getItemByItemId(key), -itemObj.getInt(key));
		}
	}

	public void operItemCount(ItemValue item, int val) {
		if (item == null) {
			return;
		}
		item.setCount(item.getCount() + val);
		if (item.getCount() <= 0) {
			inventory.remove(item.getItem().getUuid(), item.getItem().getItemId());
			userProfile.getInsideEventMgr().onUserItemChanged(item, ChangedType.REMOVE);
		} else {
			userProfile.getInsideEventMgr().onUserItemChanged(item, ChangedType.MODIFY);
		}
	}

	/**
	 * 物品数量是否足够
	 */
	public boolean isItemEnough(String itemId, int count) {
		return getItemCount(itemId) >= count;
	}

	/**
	 * 物品是否都足够
	 */
	public boolean isItemEnough(ISFSObject itemObj) {
		for (String key : itemObj.getKeys()) {
			if (!isItemEnough(key, itemObj.getInt(key))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 获取所有的itemId对应物品的数量
	 */
	private int getItemCount(String itemId) {
		if (!isItemExistsByItemId(itemId)) {
			return 0;
		}
		final int[] count = { 0 };
		inventory.column(itemId).values().forEach(p -> {
			count[0] += p.getCount();
		});
		return count[0];
	}

	public ItemResponse deleteItem(ItemValue iv, Table<String, String, ItemValue> data) {
		AbstractItem item = iv.getItem();
		data.remove(item.getUuid(), item.getItemId());
		ItemResponse ref = new ItemResponse();
		ref.addRemove(item.getUuid());
		return ref;
	}

	/**
	 * 将一堆物品分离出一个
	 */
	public ItemValue partItem(ItemValue iv) {
		if (iv.getCount() > 1) {
			// 分离一个
			iv.setCount(iv.getCount() - 1);
			// 新建一个东西
			AbstractItem abstractItem = new ItemBuilder().genericUUID().setItemId(iv.getItem().getItemId()).build();
			ItemValue newIV = new ItemValue(abstractItem, 1);
			inventory.put(abstractItem.getUuid(), abstractItem.getItemId(), newIV);
			return newIV;
		} else {
			return iv;
		}
	}

	/**
	 * 融合物品至背包中<br>
	 * 融合不成功表示iv作为单独整体被放置于背包中<br>
	 * 融合成功表示iv将被移除,融合至原有物品中,使原有物品数量+1
	 */
	public ItemValue mergeItem(ItemValue iv) {
		String itemId = iv.getItem().getItemId();
		String uuid = iv.getItem().getUuid();
		ItemValue canMergeItem = inventory.column(itemId).values().stream().filter(p -> {
			if (p.getItem().getUuid().equals(uuid)) {
				return false;
			}
			if (!p.getItem().getItemId().equals(itemId)) {
				return false;
			}
			return p.getItem().canStack();
		}).findAny().orElse(null);
		if (canMergeItem != null) {
			// 找到可融合项,融合
			canMergeItem.setCount(canMergeItem.getCount() + 1);
			inventory.remove(iv.getItem().getUuid(), iv.getItem().getItemId()); // 删掉之前的物品
			// 
			return canMergeItem;
		} else {
			return null;
		}
	}

	public void initialize() {
		if (!isInit()) {
			ISFSArray ret = PlayerConfig.getGameInitInventory();
			com.itita.ww2.redis.cache.Table t = UserService.getInstance().getCacheUserDataTable();
			t.setValue(RedisUtil.genericKey(userProfile.getUid()), "inventory", ret.toJson());
		}
		load();
		userProfile.getRewardManager().addListener(new UserRewardImpl());
	}

	private boolean isInit() {
		com.itita.ww2.redis.cache.Table t = UserService.getInstance().getCacheUserDataTable();
		String data = t.getValue(RedisUtil.genericKey(userProfile.getUid()), "inventory");
		return StringUtils.isNotBlank(data) && !data.equals("null");
	}

	private void load() {
		com.itita.ww2.redis.cache.Table t = UserService.getInstance().getCacheUserDataTable();
		JsonArray arr = new JsonParser().parse(t.getValue(RedisUtil.genericKey(userProfile.getUid()), "inventory"))
				.getAsJsonArray();
		int size = arr.size();
		// 
		inventory.clear();
		// 
		for (int i = 0; i < size; ++i) {
			JsonObject obj = arr.get(i).getAsJsonObject();
			int count = 1;
			if (obj.has("count")) {
				count = obj.get("count").getAsInt();
			}
			String uuid = obj.get("uuid").getAsString();
			String itemId = obj.get("itemId").getAsString();
			boolean used = false;
			if (obj.has("used")) {
				used = obj.get("used").getAsBoolean();
			}
			AbstractItem item = new ItemBuilder().setUUID(uuid).setItemId(itemId).setUsed(used).build();
			// 
			inventory.put(uuid, itemId, new ItemValue(item, count));
		}
	}

	public void update() {
		com.itita.ww2.redis.cache.Table t = UserService.getInstance().getCacheUserDataTable();
		t.setValue(RedisUtil.genericKey(userProfile.getUid()), "inventory", toSFSArray().toJson());
	}

	public ISFSArray toSFSArray() {
		ISFSArray ret = SFSArray.newInstance();
		for (ItemValue item : inventory.values()) {
			ret.addSFSObject(item.toSFSObject());
		}
		return ret;
	}

	public void sync() {
		ISFSObject pushObj = SFSObject.newInstance();
		pushObj.putSFSArray("data", toSFSArray());
		MessageHandler.getInstance().pushUser(PushCode.INVENTORY, pushObj, userProfile.getSFSUser());
	}

	public UserProfile getUserProfile() {
		return userProfile;
	}

	class UserRewardImpl implements IUserRewardListener {

		@Override
		public Map<String, Object> onReward(RewardType type, ISFSObject params) throws Exception {
			Map<String, Object> ret = new HashMap<>();
			switch (type) {
			case MISSION_COMPLETE:
				ret = onMissionComplete(params.getUtfString("missionId"));
				break;
			case CITY_SPECIAL_PRODUCT:
				ret = onCitySpecialProduct(params.getSFSObject("fixed_output"), params.getSFSObject("week_output"));
				break;
			case LOTTERY:
				ret = onLottery(params);
				break;
			case PAYMENT:
				ret = onPayment(params);
				break;
//			case TIME_LIMIT_CARD:
//				onTimeLimitCard(params);
//				break;
			default:
				break;
			}
			return ret;
		}

		//		private void onTimeLimitCard(ISFSObject rewards) {
		//			for (String key : rewards.getKeys()) {
		//				CodeType type = CodeType.codeValueOf(key);
		//				if (type != null && type == CodeType.ITEM) {
		//					addItem(key, rewards.getInt(key));
		//				}
		//			}
		//		}

		private Map<String, Object> onPayment(ISFSObject params) throws Exception {
			String sku = params.getUtfString("sku");
			// 
			Map<String, Object> ref = new HashMap<>();
			JsonObject retObj = new JsonObject();
			Category category = PaymentConfig.getCategory(sku);
			switch (category) {
			case PACK:
				{
					ISFSObject content = PaymentConfig.getPackItems(sku);
					for (String key : content.getKeys()) {
						CodeType type = CodeType.codeValueOf(key);
						if (type != null && type == CodeType.ITEM) {
							addItem(key, content.getInt(key));
							retObj.addProperty(key, content.getInt(key));
						}
					}
				}
				break;
			case MONTHLY_CARD:
				if (params.getBool("renewals")) {
					// 续费
					ISFSObject content = PaymentConfig.getRenewalsItems(sku);
					for (String key : content.getKeys()) {
						CodeType type = CodeType.codeValueOf(key);
						if (type != null && type == CodeType.ITEM) {
							addItem(key, content.getInt(key));
							retObj.addProperty(key, content.getInt(key));
						}
					}
				}
				break;
			default:
				break;
			}
			if (retObj.size() > 0) {
				ref.put("item", retObj);
			}
			return ref;
		}

		private Map<String, Object> onLottery(ISFSObject lotteryItems) throws Exception {
			Map<String, Object> ret = new HashMap<>();
			for (String key : lotteryItems.getKeys()) {
				CodeType type = CodeType.codeValueOf(key);
				if (type != null && type == CodeType.ITEM) {
					addItem(key, lotteryItems.getInt(key));
				}
			}
			return ret;
		}

		private Map<String, Object> onMissionComplete(String missionId) throws Exception {
			Map<String, Object> ret = new HashMap<>();
			ISFSObject rewardObj = MissionConfig.getRewardComplete(missionId);
			JsonObject retObj = new JsonObject();

			for (String key : rewardObj.getKeys()) {
				if (!key.startsWith("I")) {
					continue;
				}
				int count = 0;
				SFSDataWrapper item = rewardObj.get(key);
				if (item.getTypeId() == SFSDataType.SFS_OBJECT) {
					// 数量随机
					ISFSObject itemObj = (ISFSObject) item.getObject();
					int min = itemObj.getInt("min");
					int max = itemObj.getInt("max");
					count = MathUtil.RandomInt(min, max);

				} else if (item.getTypeId() == SFSDataType.INT) {
					count = (int) item.getObject();
				}
				// 数量0个就不加了哈哈
				if (count == 0) {
					continue;
				}
				// 批量添加
				addItem(key, count);
				retObj.addProperty(key, count);
			}
			ret.put("item", retObj);
			return ret;
		}
	}

	private Map<String, Object> onCitySpecialProduct(ISFSObject fixed_output, ISFSObject week_output) throws Exception {
		boolean flag = false;
		for (String key : fixed_output.getKeys()) {
			CodeType type = CodeType.codeValueOf(key);
			if (type == CodeType.ITEM) {
				addItem(key, fixed_output.getInt(key));
				flag = true;
			}
		}
		for (String key : week_output.getKeys()) {
			CodeType type = CodeType.codeValueOf(key);
			if (type == CodeType.ITEM) {
				addItem(key, week_output.getInt(key));
				flag = true;
			}
		}
		if (flag) {
			//			update();
			//			sync(ItemResponse.merge(responseList));
		}
		return null;
	}
}
